package com.only4play.session.local;

import com.only4play.auth.AccessTokenContent;
import com.only4play.auth.CodeContent;
import com.only4play.auth.ExpirationPolicy;
import com.only4play.session.AccessTokenManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.time.Instant;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author liyuncong
 * @version 1.0
 * @file LocalAccessTokenManager
 * @brief 本地访问令牌管理
 * @details 本地访问令牌管理
 * @date 2023-11-18
 *
 * Edit History
 * ----------------------------------------------------------------------------
 * DATE                     NAME               DESCRIPTION
 * 2023-11-18               liyuncong          Created
 */
@Slf4j
@Component
public class LocalAccessTokenManager implements AccessTokenManager, ExpirationPolicy {

//    @Value("${sso.timeout}")
    private int timeout = 600;

    private Map<String, DummyAccessToken> accessTokenMap = new ConcurrentHashMap<>();
    private Map<String, Set<String>> tgtMap = new ConcurrentHashMap<>();

    @Override
    public void create(String accessToken, AccessTokenContent accessTokenContent) {
        long expired = Instant.now().plusSeconds(getExpiresIn()).toEpochMilli();
        accessTokenMap.put(accessToken, new DummyAccessToken(accessTokenContent, expired));
        tgtMap.computeIfAbsent(accessTokenContent.getCodeContent().getTgt(), a -> new HashSet<>()).add(accessToken);
    }

    @Override
    public boolean refresh(String accessToken) {
        DummyAccessToken dummyAccessToken = accessTokenMap.getOrDefault(accessToken, null);
        if (Objects.isNull(dummyAccessToken) || Instant.now().isAfter(Instant.ofEpochMilli(dummyAccessToken.expired))) {
            return false;
        }
        dummyAccessToken.expired = Instant.now().plusSeconds(getExpiresIn()).toEpochMilli();
        return true;
    }

    @Override
    public AccessTokenContent get(String accessToken) {
        DummyAccessToken dummyAccessToken = accessTokenMap.getOrDefault(accessToken, null);
        if (Objects.isNull(dummyAccessToken) || Instant.now().isAfter(Instant.ofEpochMilli(dummyAccessToken.expired))) {
            return null;
        }
        return dummyAccessToken.accessTokenContent;
    }

    @Override
    public void remove(String tgt) {
        Set<String> accessTokenSet = tgtMap.remove(tgt);
        if (CollectionUtils.isEmpty(accessTokenSet)) {
            return;
        }
        accessTokenSet.forEach(accessToken -> {
            DummyAccessToken dummyAccessToken = accessTokenMap.getOrDefault(accessToken, null);
            if (Objects.isNull(dummyAccessToken) || Instant.now().isAfter(Instant.ofEpochMilli(dummyAccessToken.expired))) {
                return;
            }
            CodeContent codeContent = dummyAccessToken.accessTokenContent.getCodeContent();
            if (Objects.isNull(codeContent) || !codeContent.isSendLogoutRequest()) {
                return;
            }
            log.info("send logout request, accessToken: {}, url: {}", accessToken, codeContent.getRedirectUri());
            sendLogoutRequest(codeContent.getRedirectUri(), accessToken);
        });
    }

    /**
     * accessToken时效为session的1/2
     *
     * @return 时效
     */
    @Override
    public int getExpiresIn() {
        return timeout / 2;
    }

    @Override
    public void verifyExpired() {
        accessTokenMap.forEach((accessToken, dummyAccessToken) -> {
            if (Instant.now().isAfter(Instant.ofEpochMilli(dummyAccessToken.expired))) {
                accessTokenMap.remove(accessToken);
                log.info("accessToken expired, accessToken: {}", accessToken);
            }
        });
    }

    private static class DummyAccessToken {
        private final AccessTokenContent accessTokenContent;
        private long expired;

        public DummyAccessToken(AccessTokenContent accessTokenContent, long expired) {
            this.accessTokenContent = accessTokenContent;
            this.expired = expired;
        }
    }
}
